import base64
import io
import sqlite3
import tkinter as tk
from collections import defaultdict
from tkinter import messagebox, filedialog
from tkinter import ttk

import pygame


class FlashTypingApp:
    def __init__(self, root):
        self.root = root
        self.root.title("打字练习")

        # 初始化数据库
        self.init_db()

        # 初始化pygame混音器
        pygame.mixer.init()
        self.load_embedded_sound()

        # 错误统计
        self.error_stats = defaultdict(int)
        self.load_error_stats()

        self.default_text = """
        class Animal:
            def __init__(self, name):
                self.name = name
            
            def speak(self):
                raise NotImplementedError
        
        class Dog(Animal):
            @staticmethod
            def bark():
                return "Woof!"
            
            def speak(self):
                return f"{self.name} says {self.bark()}"
        
        def calculate(n):
            try:
                if n <= 0:
                    raise ValueError("Positive numbers only")
                return [i**2 for i in range(n) if i%2 ==0]
            except Exception as e:
                print(f"Error: {e}")
            finally:
                print("Calculation done")
        
        def main():
            for i in range(3):
                if i == 1:
                    continue
                print(f"Loop: {i}")
        
            dog = Dog("Buddy")
            print(dog.speak())
            print(calculate(5))
        
        if __name__ == "__main__":
            main()

        """

        self.practice_text = self.default_text
        self.current_pos = 0
        self.skip_chars = {' ', '\t', '\n'}
        self.correct_count = 0
        self.wrong_count = 0
        self.current_char = ""
        self.blink_id = None
        self.flash_id = None
        self.blink_state = False

        # 设置全屏
        self.root.attributes('-fullscreen', True)
        self.root.bind("<F11>", self.toggle_fullscreen)
        self.root.bind("<Escape>", self.exit_fullscreen)
        self.root.bind("<Key>", self.handle_key_press)

        # 创建界面
        self.setup_ui()

    def init_db(self):
        """初始化SQLite数据库"""
        self.conn = sqlite3.connect('typing_stats.db')
        self.cursor = self.conn.cursor()

        # 创建错误统计表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS error_stats (
                char TEXT PRIMARY KEY,
                count INTEGER DEFAULT 0
            )
        ''')

        # 创建字符输入统计表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS char_stats (
                char TEXT PRIMARY KEY,
                total_input INTEGER DEFAULT 0
            )
        ''')

        # 创建练习文本表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS practice_texts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                content TEXT,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        self.conn.commit()

    def load_error_stats(self):
        """从数据库加载错误统计"""
        self.cursor.execute('SELECT char, count FROM error_stats')
        for char, count in self.cursor.fetchall():
            self.error_stats[char] = count

    def save_error_stat(self, char):
        """保存错误统计到数据库"""
        self.cursor.execute('''
            INSERT OR REPLACE INTO error_stats (char, count)
            VALUES (?, COALESCE((SELECT count FROM error_stats WHERE char = ?), 0) + 1)
        ''', (char, char))
        self.conn.commit()

    def save_char_input(self, char):
        """保存字符输入统计"""
        self.cursor.execute('''
            INSERT OR REPLACE INTO char_stats (char, total_input)
            VALUES (?, COALESCE((SELECT total_input FROM char_stats WHERE char = ?), 0) + 1)
        ''', (char, char))
        self.conn.commit()

    def clear_stats(self):
        """清除所有统计数据和错误率记录"""
        # 重置内存中的统计
        self.correct_count = 0
        self.wrong_count = 0
        self.error_stats = defaultdict(int)
        # 更新字符统计显示
        self.load_and_display_char_stats()
        # 重置数据库中的统计
        self.cursor.execute('DELETE FROM error_stats')
        self.cursor.execute('DELETE FROM char_stats')
        self.conn.commit()

        # 重置UI显示
        self.status_label.config(text="统计已重置，按下键盘开始练习...", foreground="gray")
        self.stats_label.config(text="正确: 0 | 错误: 0 | 进度: 0%")

        # 清除所有高亮标记
        self.text_preview.config(state=tk.NORMAL)
        self.text_preview.tag_remove("error_highlight", "1.0", tk.END)
        self.text_preview.config(state=tk.DISABLED)

        # 重置练习位置
        self.current_pos = 0
        self.update_text_preview()
        messagebox.showinfo("成功", "所有统计数据已清除！")

    def save_practice_text(self, text):
        """保存练习文本到数据库"""
        self.cursor.execute('INSERT INTO practice_texts (content) VALUES (?)', (text,))
        self.conn.commit()

    def load_embedded_sound(self):
        # 这是一个简单的按键音效的base64编码
        sound_base64 = """
        SUQzAwAAAABCMVREQVQAAAAFAAAAMTIwM1RJTUUAAAAFAAAAMTQ1N1BSSVYAABj3AABYTVAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS41LWMwMjEgNzkuMTU1MjQxLCAyMDEzLzExLzI1LTIxOjEwOjQwICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBETT0iaHR0cDovL25zLmFkb2JlLmNvbS94bXAvMS4wL0R5bmFtaWNNZWRpYS8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpiZXh0PSJodHRwOi8vbnMuYWRvYmUuY29tL2J3Zi9iZXh0LzEuMC8iCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OWI0MWNhNTMtNTM4Yi00Y2ViLTk3YjctZmE1YzI3Y2EzZmZkIgogICB4bXBNTTpEb2N1bWVudElEPSJlMTM1YjgwNS1kMjMzLTQwZWYtZTM1OC1iZWQ0MDAwMDAwNDkiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNGVlMTgyNi05N2MwLTRiZjgtOTU4Yi0xNjJmMDExMzIwMjIiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMTQtMDMtMTJUMTQ6NTc6NTEtMDQ6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDE0LTAzLTEyVDE0OjU3OjUxLTA0OjAwIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAxNC0wMy0xMlQxNDo1NzoyMS0wNDowMCIKICAgeG1wRE06YXVkaW9TYW1wbGVSYXRlPSI0NDEwMCIKICAgeG1wRE06YXVkaW9TYW1wbGVUeXBlPSIxNkludCIKICAgeG1wRE06YXVkaW9DaGFubmVsVHlwZT0iU3RlcmVvIgogICB4bXBETTpzdGFydFRpbWVTY2FsZT0iMzAwMDAiCiAgIHhtcERNOnN0YXJ0VGltZVNhbXBsZVNpemU9IjEwMDEiCiAgIGRjOmZvcm1hdD0iTVAzIgogICBiZXh0OmRlc2NyaXB0aW9uPSJPRkZJQ0UgQ09NUFVURVIgS0VZQk9BUkQgUFJFU1MgU0lOR0xFIEtFWSBIQVJEIDAxIgogICBiZXh0Om9yaWdpbmF0b3I9IkFkb2JlIFN5c3RlbXMgSW5jIgogICBiZXh0Om9yaWdpbmF0aW9uRGF0ZT0iMjAxNC0wMy0wNSIKICAgYmV4dDpvcmlnaW5hdGlvblRpbWU9IjIwOjAyOjM0IgogICBiZXh0OnRpbWVSZWZlcmVuY2U9IjAiCiAgIGJleHQ6dmVyc2lvbj0iMSI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSIzOTNkODlmYy00YzI1LTZlZWItOWQxYi0yMGMxMDAwMDAwNzYiCiAgICAgIHN0RXZ0OndoZW49IjIwMTQtMDMtMTJUMTQ6NTc6NTEtMDQ6MDAiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIEFkb2JlIE1lZGlhIEVuY29kZXIgQ0MgKE1hY2ludG9zaCkiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9IjMzNmM3Y2Q0LWJmMTQtODNkOS1iMzE0LWM5OWUwMDAwMDA3NiIKICAgICAgc3RFdnQ6d2hlbj0iMjAxNC0wMy0wNVQyMDowMjozNC0wNTowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciBDQyAoTWFjaW50b3NoKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMjRDQ0YwRTk1MjA2ODExOTEwOUIzQjFEMUZCNUQxNCIKICAgICAgc3RFdnQ6d2hlbj0iMjAxMy0wNC0zMFQxMTo0MToyMC0wNDowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciA1LjUuMCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL21ldGFkYXRhOy9jb250ZW50Ii8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjFCMUNDODI0OTkyMDY4MTE5MTA5QjNCMUQxRkI1RDE0IgogICAgICBzdEV2dDp3aGVuPSIyMDEzLTA0LTMwVDEyOjEwOjM1LTA0OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIDUuNS4wIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249Im1vZGlmaWVkIgogICAgICBzdEV2dDpwYXJhbWV0ZXJzPSJ1bmtub3duIG1vZGlmaWNhdGlvbnMiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9ImJkZDc2N2M1LWVjZmYtNGFiNS03ZTgxLTZhMzQwMDAwMDA3NiIKICAgICAgc3RFdnQ6d2hlbj0iMjAxNC0wMy0wNVQxOTozNDowNi0wNTowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciBDQyAoTWFjaW50b3NoKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4ZjQxMjU1OS1iZWY1LTQ3MjgtYWU5Mi1jYzFhNDdjMjMyNWQiCiAgICAgIHN0RXZ0OndoZW49IjIwMTQtMDMtMDVUMjA6MDI6MzQtMDU6MDAiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIEFkb2JlIE1lZGlhIEVuY29kZXIgQ0MgKE1hY2ludG9zaCkiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6M2Y5NzMyNTAtMDNmOS00YjhhLTgyNDUtODA3Y2QzMjc4NjQwIgogICAgICBzdEV2dDp3aGVuPSIyMDE0LTAzLTA1VDIwOjAyOjM0LTA1OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIENDIChNYWNpbnRvc2gpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvbWV0YWRhdGEiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MGFiMTlhNzgtNmYzMi00YmU4LTg1NWYtYjY5MzkxODI2MGFlIgogICAgICBzdEV2dDp3aGVuPSIyMDE0LTAzLTEyVDE0OjU3OjUxLTA0OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIENDIChNYWNpbnRvc2gpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjliNDFjYTUzLTUzOGItNGNlYi05N2I3LWZhNWMyN2NhM2ZmZCIKICAgICAgc3RFdnQ6d2hlbj0iMjAxNC0wMy0xMlQxNDo1Nzo1MS0wNDowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciBDQyAoTWFjaW50b3NoKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL21ldGFkYXRhIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICAgPHhtcE1NOkRlcml2ZWRGcm9tCiAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjNmOTczMjUwLTAzZjktNGI4YS04MjQ1LTgwN2NkMzI3ODY0MCIKICAgIHN0UmVmOmRvY3VtZW50SUQ9IjI5ZTA5MTU5LTc1NWYtZjA0MC0zZjRlLWFmMzYwMDAwMDA0OSIKICAgIHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MGQ4OTFjYi0xN2RkLTRjOGEtOGJhMS0xMDg0Mjk0NTBiODkiLz4KICAgPHhtcERNOnN0YXJ0VGltZWNvZGUKICAgIHhtcERNOnRpbWVGb3JtYXQ9IjI5OTdEcm9wVGltZWNvZGUiCiAgICB4bXBETTp0aW1lVmFsdWU9IjAwOzAwOzAwOzAwIi8+CiAgIDx4bXBETTphbHRUaW1lY29kZQogICAgeG1wRE06dGltZVZhbHVlPSIwMDswMDswMDswMCIKICAgIHhtcERNOnRpbWVGb3JtYXQ9IjI5OTdEcm9wVGltZWNvZGUiLz4KICAgPHhtcERNOmR1cmF0aW9uCiAgICB4bXBETTp2YWx1ZT0iNCIKICAgIHhtcERNOnNjYWxlPSIxMDAxLzMwMDAwIi8+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4AVElUMwAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKVRQVUIAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlXT0FSAAAAFwAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlURU5DAAAAGAAAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVEFMQgAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKVRZRVIAAAAFAAAAMjAyMVRQRTIAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlUSVQyAAAAGAAAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVENPTgAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKUNPTU0AAAAcAAAAY2hpANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVFBFMQAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/++BAAAALMWlEdTGAArstKI6mMABc7hdx2PeAA53C7jse8ACLqJIRIib7/wAAOAwTn7RIEh00uvfscEg8bAgOjJLJ7RIJlSQTObXr7/l/YcqdiWT7CWrOCZEOYA4VpgAwHqiQIAkHnXWddf9FizqUmcoscbEg8qdmZmv+j/0pzZ2vxe+wscbMz+x2fvzSlMpf6Odde/i9ffGFixzVjm3vjDmrHG17+LFmnB51169e/i+9Fjmr1/3v87e/0Xv/RYscptKTf5pSZ295osc04ckP90B0cPzeIjlg/gA7///Ef///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////xdRJCJETff+AABwGCc/aJAkOml179jgkHjYEB0ZJZPaJBMqSCZza9ff8v7DlTsSyfYS1ZwTIhzAHCtMAGA9USBAEg866zrr/osWdSkzlFjjYkHlTszM1/0f+lObO1+L32FjjZmf2Oz9+aUplL/Rzrr38Xr74wsWOasc298Yc1Y42vfxYs04POuvXr38X3osc1ev+9/nb3+i9/6LFjlNpSb/NKTO3vNFjmnDkh/ugOjh+bxEcsH8AHf//4jYXmLGKENzFBIppppq8ki4J+YJyCvj8L4JmXghZ2iuEkNMmamIAPx4cJPI5doiGOCpb3aOZ2nfhuagiItiMcvJkEFvv34SMY55rKOMklJgnAoNa/3RKHW1KBXN5HRnjnEUH1n/+GiTTaTcH5dKJc/0wwKyJ////tYZ9vV50a5wQI6jV6smhK1WLi+NU//+srVmNTqPwY8Nj1L2FnvH3vEVe3v/7////7Ym2PatjKN0xLDaXNsKc5BXqtsdOJhSqtRx1ZMPSzHd//////////////mLelpsb+8bz9fW/////5L39YObSwIbC8xYxQhuYoJFNNNNXkkXBPzBOQV8fhfBMy8ELO0VwkhpkzUxAB+PDhJ5HLtEQxwVLe7RzO078NzUERFsRjl5Mggt9+/CRjHPNZRxkkpME4FBrX+6JQ62pQK5vI6M8c4ig+s//w0SabSbg/LpRLn+mGBWRP///9rDPt6vOjXOCBHUavVk0JWqxcXxqn//1lasxqdR+DHhsepews94+94ir29//f////bE2x7VsZRumJYbS5thTnIK9VtjpxMKVVqOOrJh6WY7v/////////////8xb0tNjf3jefr63/////Je/rBzaWBD/++BAAAALFldY7mMAAMvK6x3MYAAcFZ9VvYwAO4Kz6rexgAcpEsvFsmIlkslkstqRVMdYNfBgIuC0mkuseE7rjMlU4UbaY16Lqea0rC15rzWYOdp/puMWqGG41PF3oYkhdWvq5LqbOt4OEPGCiVio3UUHTVaNZ6u40wIS2zsQG9S+ZTnhqrS97+vyLyFqVbEggaRONkO62Vf8f3+Xd69wpuW4tZh1wkNqb/y/eP465////vUqazIW5O06MFNep8pT+suZZb/X///vX//P9I1TJQJlz7L5QCsCi75M+LZF6otSlXJVItTkPEw4JPlRUFf////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8pEsvFsmIlkslkstqRVMdYNfBgIuC0mkuseE7rjMlU4UbaY16Lqea0rC15rzWYOdp/puMWqGG41PF3oYkhdWvq5LqbOt4OEPGCiVio3UUHTVaNZ6u40wIS2zsQG9S+ZTnhqrS97+vyLyFqVbEggaRONkO62Vf8f3+Xd69wpuW4tZh1wkNqb/y/eP465////vUqazIW5O06MFNep8pT+suZZb/X///vX//P9I1TJQJlz7L5QCsCi75M+LZF6otSlXJVItTkPEw4JPlRUFSSUm243G2k4n8mc05ToBKL/IPM2lrcZCslUMUXytlShr7rLRch5l4yas+t2jootfU14gmau1mGXlmpG/LlSe9AVlQ0vCBBl6WuoMqWy6ZdmclNSG3dpKtDEXFlN9yYpNyaxDUP3s3Fp357Ua0u6Fy5d0AwmUS2UxmIOVP3J7uVqrO4zNmlw5lbrWrmr8RlztX4e5GZU/z/a3j+WVLjjWr3rku7hlrUZucuV5qNQ9cm525DW3Sdaaxna1rvKupVjO25VWtTU3hWl0ajVNVlMVu5dpbMamcaWGY1Kp7mseVqZCSSk23G420nE/kzmnKdAJRf5B5m0tbjIVkqhii+VsqUNfdZaLkPMvGTVn1u0dFFr6mvEEzV2swy8s1I35cqT3oCsqGl4QIMvS11BlS2XTLszkpqQ27tJVoYi4spvuTFJuTWIah+9m4tO/PajWl3QuXLugGEyiWymMxByp+5PdytVZ3GZs0uHMrda1c1fiMudq/D3IzKn+f7W8fyypcca1e9cl3cMtajNzlyvNRqHrk3O3Ia26TrTWM7Wtd5V1KsZ23Kq1qam8K0ujUapqspit3LtLZjUzjSwzGpVPc1jytTIT/++BAAAAP+mXP6e97bv3Muf09723PLMMjrGGLYeWYZHWMMWwMlJuSNyNpJizBFHaWwBJJkBiPJUiSklHqV47gEokIpJPkIH2EZWRNQbJqokTUuSqOI5WAq0aX4/lCrVS+J0fxCx6WVDiQhcvzuABwvgNUvD9DUafx1tB5k5PUI8cyDbxbTpQ1gseyuQpmQ5CorUPphVhbkKKEsMZ+xGAUzi5xnuoUGWLlUvs1xaj6NWWumtlrBYSUqhyVrDNTONyvf8RcWxebxvJukbEKzE5WtVhcW6+dPbY38b/alFd7EzTMSCrcY76Nu1Yr2L4Us3+JYtR0Vf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wyUm5I3I2kmLMEUdpbAEkmQGI8lSJKSUepXjuASiQikk+QgfYRlZE1BsmqiRNS5Ko4jlYCrRpfj+UKtVL4nR/ELHpZUOJCFy/O4AHC+A1S8P0NRp/HW0HmTk9QjxzINvFtOlDWCx7K5CmZDkKitQ+mFWFuQooSwxn7EYBTOLnGe6hQZYuVS+zXFqPo1Za6a2WsFhJSqHJWsM1M43K9/xFxbF5vG8m6RsQrMTla1WFxbr509tjfxv9qUV3sTNMxIKtxjvo27VivYvhSzf4li1HRV///////////////////////////////////////////////////////////8JN3bWyRpAApBIlhYabwvYQjLfpdpNpgAgBdhI4UGoKichEqJgKPL+sid6hf2I2s7sMxmN5wC6tJjOXMTo2IqhONN3i6VW3R9bZraNvd4kutWpzHZ65dLtZttaQNUYz896BdZ62KP+14pLN6e23Q03DqVPf/S/26TdYSbu2tkjSABSCRLCw03hewhGW/S7SbTABAC7CRwoNQVE5CJUTAUeX9ZE71C/sRtZ3YZjMbzgF1aTGcuYnRsRVCcabvF0qtuj62zW0be7xJdatTmOz1y6Xazba0gaoxn570C6z1sUf9rxSWb09tuhpuHUqe/+l/t0m6z/++BAAAAP+T5Iaw9C4X8nyQ1h6FwHHFsZjDBHCOOLYzGGCOERq2WySSRttplIHJrIChrKZaJUYVVLTR5gbbpaH8fpUGCYBlEGMfEPDS5wnzapm5agYYomM10tbdEmgSFqDwPgbSdanIOC1iEHrCLuMDtYHMh6qPGWvX1VlbVPxcV/V//cf9xH5KFUVuCrhUWWCrziHzyjxGdqW5JmPdLJrOOwaK//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////iNWy2SSSNttMpA5NZAUNZTLRKjCqpaaPMDbdLQ/j9KgwTAMogxj4h4aXOE+bVM3LUDDFExmulrbok0CQtQeB8DaTrU5BwWsQg9YRdxgdrA5kPVR4y16+qsrap+Liv6v/7j/uI/JQqitwVcKiywVecQ+eUeIztS3JMx7pZNZx2DRX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////bjTTJTKgAw5pKlKHAM0ITKvkLq1YHx7G6JJyp6a2nBOFHNFVQCsOGiiAwQZUJqN/poVF8bw76ta43GmmSmVABhzSVKUOAZoQmVfIXVqwPj2N0STlT01tOCcKOaKqgFYcNFEBggyoTUb/TQqL43h31a1z/++BAAAAP/ABFhQAACBoACLCgAAEdaiVP+PmQC61Eqf8fMgH////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8MaMLUMUrM2MFConHI7HZHI2VWSAug+zjGOLAdZRw2yot5YycHcOYQ1MV8ZcaI4zYlSJlwZ8hhYHCmaGZuKXIoVJQSRJ/d2JIVoLgKAz4zAoAWQGry+7+NMZgfYyZHiC4cGDYgMsEeAIQDQJ33x7HeTBOGxomAVQBUYuYYwiQ5ArTZrbFxkyoTBUTdMMFlIliACAggAAUcQetvtfNXQUya3QPkwMuUTcdZEz44ERlxbBSfZDbbaaEUKhfJxZuX0mL6adQeuFzgyiZFxlyuXzcUvNS+JQHZ////7f///9Y5g5hUN1IOgyabm4eIwxowtQxSszYwUKiccjsdkcjZVZIC6D7OMY4sB1lHDbKi3ljJwdw5hDUxXxlxojjNiVImXBnyGFgcKZoZm4pcihUlBJEn93YkhWguAoDPjMCgBZAavL7v40xmB9jJkeILhwYNiAywR4AhANAnffHsd5ME4bGiYBVAFRi5hjCJDkCtNmtsXGTKhMFRN0wwWUiWIAICCAABRxB62+181dBTJrdA+TAy5RNx1kTPjgRGXFsFJ9kNttpoRQqF8nFm5fSYvpp1B64XODKJkXGXK5fNxS81L4lAdn////t////1jmDmFQ3Ug6DJpubh4j/++BAAAALFWzWdj3gAMotms7HvAAcMYMvPYyAK4YwZeexkAU0YnlElWYkcQFVVWqatCRgkKHEPQqSYvRwqMnBmjxMMkS7FxNMn6Fkgci4RWxlTNXadOVh2pToUBCIlYzXuNRqLe4pNOKlTMTeXo/WXHbuaMJeXLcm1Ku3Sl2htfnO8Ky6pVsLntTt2bT2z//8lST0qTkQjDSz1UrKfsOLAis0H+vrq38RwiqN+uWplix49s0s+/zqC9//+v/8u52Y6lloiNimSymfW0fySrBxBZVLiM12zXHr9//////1tGjemL73rDcH3/sPPwaI///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////5oxPKJKsxI4gKqqtU1aEjBIUOIehUkxejhUZODNHiYZIl2LiaZP0LJA5FwitjKmau06crDtSnQoCERKxmvcajUW9xSacVKmYm8vR+suO3c0YS8uW5NqVdulLtDa/Od4Vl1SrYXPanbs2ntn//5KknpUnIhGGlnqpWU/YcWBFZoP9fXVv4jhFUb9ctTLFjx7ZpZ9/nUF7//9f/5dzsx1LLREbFMllM+to/klWDiCyqXEZrtmuPX7//////raNG9MX3vWG4Pv/Yefg0RWWb9mvrVucJ/2GpiBdMvWaIgwS4jO25NqwEyFTMZs/7aMjbI2RH5nT/MPgeYcGQKUtcceca7DNhZLsw4giMUYwzDLEXK3s4ypl0Wa0waUJtnsaXxW4oMo3By0W0cp1ZXNyaXrtTlX42KH52ljshm7aYqRyXS8qNynSc7GM1GnQc+7iQ3ecFpqt0fUy6uaXNahUndqNTMOymdmoelMegb5TYzx3Kd50XdWrW5m1lEp7Va/Vpb+qtLZy3zWNy1+VW9KZTlj81Lrt7LKrS4483V1+ONmUxnXzVrKVU25DjcsUTJZZZv2a+tW5wn/YamIF0y9ZoiDBLiM7bk2rATIVMxmz/toyNsjZEfmdP8w+B5hwZApS1xx5xrsM2FkuzDiCIxRjDMMsRcrezjKmXRZrTBpQm2expfFbigyjcHLRbRynVlc3Jpeu1OVfjYofnaWOyGbtpipHJdLyo3KdJzsYzUadBz7uJDd5wWmq3R9TLq5pc1qFSd2o1Mw7KZ2ah6Ux6BvlNjPHcp3nRd1atbmbWUSntVr9Wlv6q0tnLfNY3LX5Vb0plOWPzUuu3ssqtLjjzdXX442ZTGdfNWspVTbkONyxRMlj/++BAAAAP+TZG6xjC0ZymyN1jGFoHzHMZBgSpAFeOYyDAlSBQlJyNtuNIkoTkUUFmcCMQdIKBQJBAkeS2qtoQmncFlqyQMWGYaZSmLAtecd6AZLepcbOL6vy0qKP1QsNdXvGVMSZzJp6rhKWcxmAntBB4ZkMTRNQAwI1pYVsCVzYZ2lmYze+rZwyrV39fagn7Ea7q/j+WOWdJPb/uXbOGWXfqmnogYHYdWwO6Z5NXKuyKV/uh1Z2qv1f/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////qEpORttxpElCciigszgRiDpBQKBIIEjyW1VtCE07gstWSBiwzDTKUxYFrzjvQDJb1LjZxfV+WlRR+qFhrq94ypiTOZNPVcJSzmMwE9oIPDMhiaJqAGBGtLCtgSubDO0szGb31bOGVau/r7UE/YjXdX8fyxyzpJ7f9y7Zwyy79U09EDA7Dq2B3TPJq5V2RSv90OrO1V+r//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+pUAEBMBJVhlHASnphJ//////+JAY3//////////////////////////////////////////////////////UqACAmAkqwyjgJT0wk///////EgMb/++BAAAAP/ABLgAAACZyACXAAAAEKgAEuAAAAIAAAJcAAAAT/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////++BAAAAP/ABLgAAACZyACXAAAAEKgAEuAAAAIAAAJcAAAAT///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9UQUfVvrOky9iyxChzYy5jaGluYXouY29tKSAgICAgICDVvrOky9iyxChzYy5jaGluYXouY29tKSAgICAgICDVvrOky9iyxChzYy5jaGluYXouY29tKSAgICAgICAyMDIx1b6zpMvYssQoc2MuY2hpbmF6LmNvbSkgICAgICAgDA==
        """  # 这里应该是完整的base64编码音频数据
        try:
            sound_data = base64.b64decode(sound_base64)
            sound_file = io.BytesIO(sound_data)
            self.key_sound = pygame.mixer.Sound(sound_file)
        except Exception as e:
            print(f"无法加载内置音效: {e}")
            self.key_sound = pygame.mixer.Sound(buffer=bytearray(0))

    def setup_ui(self):
        """设置用户界面"""
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 标题
        title_label = ttk.Label(
            main_frame,
            text="编程打字练习",
            font=("Microsoft YaHei", 24, "bold")
        )
        title_label.pack(pady=(20, 40))

        # 控制按钮框架
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(fill=tk.X, pady=(0, 40))
        # 添加字符统计显示区域
        self.stats_frame = ttk.Frame(main_frame)
        self.stats_frame.pack(fill=tk.X, pady=(10, 20))

        self.char_stats_label = tk.Text(
            self.stats_frame,
            height=5,
            wrap=tk.WORD,
            font=("Consolas", 10),
            state=tk.DISABLED,
            bg="#f0f0f0",
            relief=tk.FLAT
        )
        self.char_stats_label.pack(fill=tk.BOTH, expand=True)
        # 控制按钮
        ttk.Button(
            control_frame,
            text="加载文本",
            command=self.load_text_file
        ).pack(side=tk.LEFT, padx=20)

        ttk.Button(
            control_frame,
            text="重置",
            command=self.reset_practice
        ).pack(side=tk.LEFT, padx=20)

        ttk.Button(
            control_frame,
            text="清除统计",
            command=self.clear_stats
        ).pack(side=tk.LEFT, padx=20)

        ttk.Button(
            control_frame,
            text="退出全屏",
            command=self.exit_fullscreen
        ).pack(side=tk.RIGHT, padx=20)

        # 文本预览区域
        self.text_preview = tk.Text(
            main_frame,
            height=20,
            wrap=tk.WORD,
            font=("Consolas", 16),
            state=tk.DISABLED,
            bg="#f5f5f5",
            relief=tk.FLAT
        )
        self.text_preview.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))

        # 设置文本标签
        self.text_preview.tag_config("current_char", underline=True, underlinefg="red")
        self.text_preview.tag_config("correct", foreground="green")
        self.text_preview.tag_config("wrong", foreground="red")
        self.text_preview.tag_config("flash", background="red")
        self.text_preview.tag_config("error_highlight", background="yellow")

        # 统计信息
        stats_frame = ttk.Frame(main_frame)
        stats_frame.pack(fill=tk.X, pady=(0, 20))

        self.stats_label = ttk.Label(
            stats_frame,
            text="正确: 0 | 错误: 0 | 进度: 0%",
            font=("Microsoft YaHei", 12)
        )
        self.stats_label.pack()

        # 状态信息
        self.status_label = ttk.Label(
            main_frame,
            text="按下键盘开始练习...",
            font=("Microsoft YaHei", 12),
            foreground="gray"
        )
        self.status_label.pack(pady=(0, 20))

        # 初始化文本显示
        self.update_text_preview()
        self.start_blinking()

    def load_and_display_char_stats(self):
        """加载并显示字符统计信息"""
        # 从数据库获取所有字符的统计
        self.cursor.execute('''
            SELECT 
                es.char, 
                es.count as error_count,
                cs.total_input,
                CASE 
                    WHEN cs.total_input > 0 THEN ROUND((es.count * 100.0 / cs.total_input), 1)
                    ELSE 0 
                END as error_rate
            FROM error_stats es
            JOIN char_stats cs ON es.char = cs.char
            ORDER BY error_rate DESC
        ''')

        stats = self.cursor.fetchall()

        # 准备显示内容
        stats_text = "字符统计（错误率从高到低）：\n"
        stats_text += "{:<5} {:<8} {:<10} {:<10}\n".format("字符", "错误次数", "总输入次数", "错误率(%)")
        stats_text += "-" * 40 + "\n"

        for char, error_count, total_input, error_rate in stats:
            stats_text += "{:<7} {:<12} {:<14} {:<10}\n".format(
                repr(char)[1:-1],  # 去掉引号
                error_count,
                total_input,
                error_rate
            )

        # 更新显示
        self.char_stats_label.config(state=tk.NORMAL)
        self.char_stats_label.delete(1.0, tk.END)
        self.char_stats_label.insert(tk.END, stats_text)
        self.char_stats_label.config(state=tk.DISABLED)

    def start_blinking(self):
        """开始闪烁当前字符的下划线"""
        if self.blink_id:
            self.root.after_cancel(self.blink_id)

        if self.current_pos < len(self.practice_text):
            self.blink_state = not self.blink_state
            if self.blink_state:
                self.text_preview.tag_config("current_char", underline=True, underlinefg="red")
            else:
                self.text_preview.tag_config("current_char", underline=False)

            self.blink_id = self.root.after(500, self.start_blinking)

    def flash_error(self):
        """显示错误闪烁"""
        if self.flash_id:
            self.root.after_cancel(self.flash_id)
            self.text_preview.tag_remove("flash", "1.0", tk.END)

        start_index = f"1.0 + {self.current_pos} chars"
        self.text_preview.tag_add("flash", start_index, f"{start_index} + 1 chars")
        self.flash_id = self.root.after(1000, lambda: self.text_preview.tag_remove("flash", "1.0", tk.END))

    def handle_key_press(self, event):
        """处理键盘按键"""
        self.play_key_sound(event)

        if self.current_pos >= len(self.practice_text):
            self.status_label.config(text="练习完成！", foreground="green")
            return

        target_char = self.practice_text[self.current_pos]

        # 跳过空格、制表符和换行符
        if target_char in self.skip_chars:
            self.current_pos += 1
            self.update_text_preview()
            return

        # 更新字符总输入次数
        self.save_char_input(target_char)

        if event.char and event.char == target_char:
            # 标记为正确
            start_index = f"1.0 + {self.current_pos} chars"
            self.text_preview.tag_add("correct", start_index, f"{start_index} + 1 chars")

            self.correct_count += 1
            self.current_pos += 1
            self.status_label.config(text="正确！继续...", foreground="green")

            if self.current_pos >= len(self.practice_text):
                self.status_label.config(text="练习完成！", foreground="green")
        elif event.char:
            # 记录错误统计
            self.error_stats[target_char] += 1
            self.save_error_stat(target_char)

            self.flash_error()

            start_index = f"1.0 + {self.current_pos} chars"
            self.text_preview.tag_add("wrong", start_index, f"{start_index} + 1 chars")

            self.wrong_count += 1
            self.status_label.config(text=f"错误！应该是 '{target_char}'", foreground="red")
        # 更新字符统计显示
        self.load_and_display_char_stats()
        self.update_text_preview()

    def play_key_sound(self, event):
        """播放按键音效"""
        if event.char and event.keysym not in ["BackSpace", "Delete", "Shift_L", "Shift_R", "Control_L", "Control_R"]:
            try:
                self.key_sound.play()
            except:
                pass

    def update_text_preview(self):
        """更新文本预览"""
        self.text_preview.config(state=tk.NORMAL)
        self.text_preview.delete(1.0, tk.END)
        self.text_preview.insert(tk.END, self.practice_text)

        # 高亮错误比例高的字符（错误率>50%）
        for i, char in enumerate(self.practice_text):
            if char not in self.skip_chars:
                # 获取该字符的错误次数和总输入次数
                self.cursor.execute('''
                    SELECT 
                        (SELECT count FROM error_stats WHERE char = ?),
                        (SELECT total_input FROM char_stats WHERE char = ?)
                ''', (char, char))
                result = self.cursor.fetchone()

                # 更健壮的处理方式
                error_count = result[0] if result and result[0] is not None else 0
                total_input = result[1] if result and result[1] is not None else 0

                if total_input > 0:  # 确保有输入记录
                    error_rate = error_count / total_input
                    if error_rate > 0.5:  # 错误率超过50%
                        start_index = f"1.0 + {i} chars"
                        self.text_preview.tag_add("error_highlight", start_index, f"{start_index} + 1 chars")

        # 高亮当前字符
        if self.current_pos < len(self.practice_text):
            # 跳过空格、制表符和换行符
            while (self.current_pos < len(self.practice_text) and
                   (self.practice_text[self.current_pos] in self.skip_chars)):
                self.current_pos += 1

            if self.current_pos < len(self.practice_text):
                start_index = f"1.0 + {self.current_pos} chars"
                self.text_preview.tag_add("current_char", start_index, f"{start_index} + 1 chars")
                self.text_preview.see(start_index)

        self.text_preview.config(state=tk.DISABLED)

        # 更新统计信息
        total_chars = len([c for c in self.practice_text if c not in self.skip_chars])
        if total_chars > 0:
            progress = (self.correct_count + self.wrong_count) / total_chars * 100
        else:
            progress = 0

        self.stats_label.config(
            text=f"正确: {self.correct_count} | 错误: {self.wrong_count} | 进度: {progress:.1f}%"
        )

    def load_text_file(self):
        """加载文本文件"""
        file_path = filedialog.askopenfilename(
            title="选择文本文件",
            filetypes=[("文本文件", "*.txt"), ("Python文件", "*.py"), ("所有文件", "*.*")]
        )

        if file_path:
            try:
                with open(file_path, "r", encoding="utf-8") as f:
                    self.practice_text = f.read()
                self.save_practice_text(self.practice_text)
                self.reset_practice()
                messagebox.showinfo("成功", "文本加载成功！")
            except Exception as e:
                messagebox.showerror("错误", f"无法加载文件:\n{str(e)}")

    def reset_practice(self):
        """重置练习"""
        self.current_pos = 0
        self.correct_count = 0
        self.wrong_count = 0
        self.status_label.config(text="按下键盘开始练习...", foreground="gray")
        self.update_text_preview()
        self.start_blinking()

    def toggle_fullscreen(self, event=None):
        """切换全屏模式"""
        self.root.attributes('-fullscreen', not self.root.attributes('-fullscreen'))

    def exit_fullscreen(self, event=None):
        """退出全屏模式"""
        self.root.attributes('-fullscreen', False)

    def __del__(self):
        """析构函数，关闭数据库连接"""
        self.conn.close()

    def run(self):
        """运行应用程序"""
        self.root.mainloop()


if __name__ == "__main__":
    root = tk.Tk()
    app = FlashTypingApp(root)
    app.run()